tree grammar graalTree;

options {
  tokenVocab=graal; 
  ASTLabelType=CommonTree;
}

@header
{
 import pt.our.angemc.datatypes.*;
}

@members
{
 public Multiplier getMultiplier(String c)
 {
  Multiplier m = null;
  
  if(c.equals("*")){ m = Multiplier.STAR; }
  else{ 
       if(c.equals("?")){m = Multiplier.QUESTION;}
        else{ 
             if(c.equals("+")){m = Multiplier.PLUS;}
              else{ 
                 if(c.equals("' '")){m = Multiplier.NONE;}
                  }  
            }
      }               
  return m;
 } 
 
 public Symbol criaSymbol(int flag,String mult,String simb,ArrayList<Production> simbs,ArrayList<String> atribs)
 {
  Symbol s = null;
  switch(flag)
  {
   	case 0 : s = criaTerminal(mult,"var",simb);break;
   	case 1 : s = criaNterminal(mult,simb,atribs);break;
   	case 2 : s = criaTerminal(mult,"res",simb);break;
   	case 3 : s = criaTerminal(mult,"sin",simb);break;
   	case 4 : s = criaSeq(mult,simbs);break;
   	case 5 : s = criaAlt(mult,simbs);break;
   	case 6 : s = criaTerminal(mult,"seq",simb);break;
   	default: break;
  }  
 return s;
 }
 
 public Terminal criaTerminal(String mult,String s, String simb)
 {
  Terminal t = null;
  
  if(s.equals("var")) { t = new Terminal(simb,getMultiplier(mult),TerminalType.OTHER,false); }
   else{
        if(s.equals("res")) { t = new Terminal(simb,getMultiplier(mult),TerminalType.OTHER,false); }
        else{
             if(s.equals("sin")) { t = new Terminal(simb,getMultiplier(mult),TerminalType.CHAR,false); }
             else
             {
              if(s.equals("seq")) { t = new Terminal(simb,getMultiplier(mult),TerminalType.SEQUENCE,false); }
             }
             }
       }        
  
  return t;
 }
 
 public NonTerminal criaNterminal(String mult,String simb,ArrayList<String> atribs)
 {
  return new NonTerminal(simb,getMultiplier(mult),false,atribs); 
 }
 
 public SymbolsSequence criaSeq(String mult,ArrayList<Production> prods)
 {
  ArrayList<Symbol> simbs = new ArrayList<Symbol>();
  for(Production p : prods)
  {for(Symbol s : p.getSymbols())
   {simbs.add(s.clone());}} 
  return new SymbolsSequence(" ",getMultiplier(mult),simbs,false); 
 }
 
 public AlternativeSymbols criaAlt(String mult,ArrayList<Production> prods)
 {
  ArrayList<Symbol> simbs = new ArrayList<Symbol>();
  for(Production p : prods)
  {for(Symbol s : p.getSymbols())
   {simbs.add(s.clone());}} 
  return new AlternativeSymbols(" ",getMultiplier(mult),simbs,false); 
 }   
}  

grammar_sq returns[Grammar g]
  : 
  { g = new Grammar();
   ArrayList<Rule> regras = new ArrayList<Rule>();}
  (r=rule
  {regras.add($r.ruleOUT);}   
     )+
  {$g = new Grammar(regras);} 
  ;

rule
 returns[Rule ruleOUT]
  :  ^( TERMINAL i=identifier a=alternatives )
     { 
      Terminal t = new Terminal($i.id, Multiplier.NONE, TerminalType.OTHER,false);
      $ruleOUT = new Rule(t,$a.alts,null,null);
     }
  |  ^( NTERMINAL i=identifier a=alternatives at=atributs)
     { 
      NonTerminal nt = new NonTerminal($i.id, Multiplier.NONE,false,null);
      $ruleOUT = new Rule(nt,$a.alts,$at.atribsIN,$at.atribsOUT);
     }	   
;

identifier
  returns[String id]
  : ID { $id = $ID.text; }  
  ;

atributs
  returns[ArrayList<Variable> atribsIN,ArrayList<Variable> atribsOUT]
  : 
   ^(ATSIN (i=ats{$atribsIN = $i.atribs;})? ) ^(ATSOUT (o=ats{$atribsOUT = $o.atribs;} )?)
  ;	
  
ats 
  returns[ArrayList<Variable> atribs]
  :{$atribs = new ArrayList<Variable>();} 
   (v=variable{ $atribs.add($v.var);} )+ 
  ;

variable
  returns[Variable var]
  : t=ID n=ID {$var = new Variable($t.text,$n.text);}
  ;		  

alternatives
 returns[ArrayList<Production> alts] 
  : {$alts = new ArrayList<Production>(); } 
    (^(ALTERNATIVE e=elements
    {
     $alts.add($e.prod);
    }
    ))+ 
  ;
  
elements
 returns[Production prod]
  :{ArrayList<Symbol> simbs = new ArrayList<Symbol>();}  
   (^(ELEMENT e=element
   {simbs.add($e.simb);}
   ))+
   {$prod = new Production(simbs);}     
  ;
  
element
 returns[Symbol simb]
 @init
 {
  int fl = 0;
 }  
  :  ^( TERMINAL s=single (m=multiplier{fl = 1;})? )
        {if(fl==1){$simb = criaSymbol(0,$m.s,$s.id,null,null);}
         else{$simb = criaSymbol(0,"' '",$s.id,null,null);}
        }
  |  ^( TERMINALT s=single (m=multiplier{fl = 1;})? )
        {if(fl==1)
         {Symbol sy = criaSymbol(0,$m.s,$s.id,null,null);
          Terminal t = new Terminal("~", Multiplier.NONE, TerminalType.CHAR,false);
          $simb = new SymbolsSequence("", Multiplier.NONE, new ArrayList<Symbol>(),false);
          ((SymbolsSequence)$simb).addSymbol(t);
          ((SymbolsSequence)$simb).addSymbol(sy);
         }
         else{   
          Symbol sy = criaSymbol(0,"' '",$s.id,null,null);
          Terminal t = new Terminal("~", Multiplier.NONE, TerminalType.CHAR,false);
          $simb = new SymbolsSequence("", Multiplier.NONE, new ArrayList<Symbol>(),false);
          ((SymbolsSequence)$simb).addSymbol(t);
          ((SymbolsSequence)$simb).addSymbol(sy);}
            }  
  |  ^( NTERMINALT s=single (m=multiplier{fl = 1;})?  )
        {if(fl==1)
         {Symbol sy = criaSymbol(1,$m.s,$s.id,null,$s.atribs);
          Terminal t = new Terminal("~", Multiplier.NONE, TerminalType.CHAR,false);
          $simb = new SymbolsSequence("", Multiplier.NONE, new ArrayList<Symbol>(),false);
          ((SymbolsSequence)$simb).addSymbol(t);
          ((SymbolsSequence)$simb).addSymbol(sy);
         }
         else{ 
          Symbol sy = criaSymbol(1,"' '",$s.id,null,$s.atribs);
          Terminal t = new Terminal("~", Multiplier.NONE, TerminalType.CHAR,false);
          $simb = new SymbolsSequence("", Multiplier.NONE, new ArrayList<Symbol>(),false);
          ((SymbolsSequence)$simb).addSymbol(t);
          ((SymbolsSequence)$simb).addSymbol(sy);
          }
        }
  |  ^( NTERMINAL s=single (m=multiplier{fl = 1;})?  )
        {if(fl==1){$simb = criaSymbol(1,$m.s,$s.id,null,$s.atribs);}
         else{ $simb = criaSymbol(1,"' '",$s.id,null,$s.atribs);}}       
  |  ^( RESERVED s=single (m=multiplier{fl = 1;})?)
        {if(fl==1){$simb = criaSymbol(2,$m.s,$s.id,null,null);}
         else{ $simb = criaSymbol(2,"' '",$s.id,null,null);}}
  |  ^( RESERVEDT s=single (m=multiplier{fl = 1;})?)
        {if(fl==1)
         {Symbol sy = criaSymbol(2,$m.s,$s.id,null,null);
          Terminal t = new Terminal("~", Multiplier.NONE, TerminalType.CHAR,false);
          $simb = new SymbolsSequence("", Multiplier.NONE, new ArrayList<Symbol>(),false);
          ((SymbolsSequence)$simb).addSymbol(t);
          ((SymbolsSequence)$simb).addSymbol(sy);
         }
         else{ 
          Symbol sy = criaSymbol(2,"' '",$s.id,null,null);
          Terminal t = new Terminal("~", Multiplier.NONE, TerminalType.CHAR,false);
          $simb = new SymbolsSequence("", Multiplier.NONE, new ArrayList<Symbol>(),false);
          ((SymbolsSequence)$simb).addSymbol(t);
          ((SymbolsSequence)$simb).addSymbol(sy);
          }
          }       
  |  ^( SIGN s=single (m=multiplier{fl = 1;})? )
        {if(fl==1){$simb = criaSymbol(3,$m.s,$s.id,null,null);}
         else{ $simb = criaSymbol(3,"' '",$s.id,null,null);}}
  |  ^( SIGNT s=single (m=multiplier{fl = 1;})? )
        {if(fl==1){
          Symbol sy = criaSymbol(3,$m.s,$s.id,null,null);
          Terminal t = new Terminal("~", Multiplier.NONE, TerminalType.CHAR,false);
          $simb = new SymbolsSequence("", Multiplier.NONE, new ArrayList<Symbol>(),false);
          ((SymbolsSequence)$simb).addSymbol(t);
          ((SymbolsSequence)$simb).addSymbol(sy);
          }
         else{ 
          Symbol sy = criaSymbol(3,"' '",$s.id,null,null);
          Terminal t = new Terminal("~", Multiplier.NONE, TerminalType.CHAR,false);
          $simb = new SymbolsSequence("", Multiplier.NONE, new ArrayList<Symbol>(),false);
          ((SymbolsSequence)$simb).addSymbol(t);
          ((SymbolsSequence)$simb).addSymbol(sy);
          }}       
  |  ^( SEQUENCE a=alternatives (m=multiplier{fl = 1;})? )
        {if(fl==1){$simb = criaSymbol(4,$m.s,null,$a.alts,null);}
         else{ $simb = criaSymbol(4,"' '",null,$a.alts,null);}}
  |  ^( SEQUENCET a=alternatives (m=multiplier{fl = 1;})? ) 
        {if(fl==1){
          Symbol sy = criaSymbol(4,$m.s,null,$a.alts,null);
          Terminal t = new Terminal("~", Multiplier.NONE, TerminalType.CHAR,false);
          $simb = new SymbolsSequence("", Multiplier.NONE, new ArrayList<Symbol>(),false);
          ((SymbolsSequence)$simb).addSymbol(t);
          ((SymbolsSequence)$simb).addSymbol(sy);
          }
         else{ 
          Symbol sy = criaSymbol(4,"' '",null,$a.alts,null);
          Terminal t = new Terminal("~", Multiplier.NONE, TerminalType.CHAR,false);
          $simb = new SymbolsSequence("", Multiplier.NONE, new ArrayList<Symbol>(),false);
          ((SymbolsSequence)$simb).addSymbol(t);
          ((SymbolsSequence)$simb).addSymbol(sy);
          }}       
  |  ^( OTHER s=single (m=multiplier{fl = 1;})?)
        {if(fl==1){$simb = criaSymbol(6,$m.s,$s.id,null,null);}
         else{$simb = criaSymbol(6,"' '",$s.id,null,null);}
        }
  |  ^( OTHERT s=single (m=multiplier{fl = 1;})?)
        {
         if(fl==1)
         {Symbol sy = criaSymbol(6,$m.s,$s.id,null,null);
          Terminal t = new Terminal("~", Multiplier.NONE, TerminalType.CHAR,false);
          $simb = new SymbolsSequence("", Multiplier.NONE, new ArrayList<Symbol>(),false);
          ((SymbolsSequence)$simb).addSymbol(t);
          ((SymbolsSequence)$simb).addSymbol(sy);
         }
         else{   
          Symbol sy = criaSymbol(6,"' '",$s.id,null,null);
          Terminal t = new Terminal("~", Multiplier.NONE, TerminalType.CHAR,false);
          $simb = new SymbolsSequence("", Multiplier.NONE, new ArrayList<Symbol>(),false);
          ((SymbolsSequence)$simb).addSymbol(t);
          ((SymbolsSequence)$simb).addSymbol(sy);}
            }
  |  ^( OTHER a=alternatives (m=multiplier{fl = 1;})?)  
        {if(fl==1){$simb = criaSymbol(5,$m.s,null,$a.alts,null);}
         else{ $simb = criaSymbol(5,"' '",null,$a.alts,null);}}
  |  ^( OTHERT alternatives (m=multiplier{fl = 1;})?)
        {if(fl==1){
          Symbol sy = criaSymbol(5,$m.s,null,$a.alts,null);
          Terminal t = new Terminal("~", Multiplier.NONE, TerminalType.CHAR,false);
          $simb = new SymbolsSequence("", Multiplier.NONE, new ArrayList<Symbol>(),false);
          ((SymbolsSequence)$simb).addSymbol(t);
          ((SymbolsSequence)$simb).addSymbol(sy);
          }
         else{ 
          Symbol sy = criaSymbol(5,"' '",null,$a.alts,null);
          Terminal t = new Terminal("~", Multiplier.NONE, TerminalType.CHAR,false);
          $simb = new SymbolsSequence("", Multiplier.NONE, new ArrayList<Symbol>(),false);
          ((SymbolsSequence)$simb).addSymbol(t);
          ((SymbolsSequence)$simb).addSymbol(sy);
          }}          
  ;  

multiplier
 returns[String s]  
  : a='*' { $s = $a.text;}
  | a='+' { $s = $a.text;}
  | a='?' { $s = $a.text;} 
  ; 

single
  returns[String id,ArrayList<String> atribs] 
  : s=STRING{$id = $s.text;} ('..' l=STRING {$id += ".." + $l.text;} )?  { $id.replace( "'", " " ); }
  | {$atribs = new ArrayList<String>();}
    i=identifier (a=atribs {$atribs = $a.atribs; })?  { $id= $i.id;}              
  ; 
  
atribs
  returns[ArrayList<String> atribs]
  : {$atribs = new ArrayList<String>();} 
    (v=var{$atribs.add($v.var);})+  
  ;	

var returns[String var]
  : a=ID {$var = $a.text;} | a=STRING {$var = $a.text;} | a=DOLARID {$var = $a.text;};  
